import os

import click
import docker
from cpuinfo import get_cpu_info
from flask import Flask, flash, render_template, redirect, request, url_for
# 导入Bcrypt加密
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, login_user, UserMixin, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy
from psutil import virtual_memory

from forms import LoginForm, ChangePasswordForm

app = Flask(__name__)
app.config['SECRET_KEY'] = 'a86d1cff6f8aec59f277386398c6f3c5'
# 数据库参数
# 配置数据路访问路径
project_dir = os.path.dirname(os.path.abspath(__file__))  # 获取项目文件夹
database_file = "sqlite:///{}".format(os.path.join(project_dir, "data.db"))  # 采用SQLITE数据库
app.config['SQLALCHEMY_DATABASE_URI'] = database_file
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 加载数据库
db = SQLAlchemy(app)
# 加载加密组件
bcrypt = Bcrypt(app)
# 加载用户登录管理组件
login_manager = LoginManager(app)
login_manager.login_view = 'login'  # 登录视图
login_manager.login_message = u"Please log in to access this page."
login_manager.login_message_category = 'info'


# 用户数据模型
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), nullable=False)
    password = db.Column(db.String(10), nullable=False)

    def __repr__(self):
        return f"User('{self.username}','{self.password}')"


# 利用flask initdb初始化数据库
@app.cli.command()
def initdb():
    db.create_all()
    click.echo('Initialized database.')


# 利用Flask fakeadmin创建一个默认的admin用户
@app.cli.command()
def fakeadmin():
    admin = User(username='admin',
                 password=bcrypt.generate_password_hash('admin'))
    db.session.add(admin)
    db.session.commit()
    click.echo('insert admin user success!')


# 提供一个回调函数，返回相应的用户对象
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


# Docker Web App Login Page
@app.route('/', methods=['GET', 'POST'])
@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated: #已经登录，则跳转
        return redirect(url_for('home'))

    form = LoginForm()
    if form.validate_on_submit() and request.method == 'POST':
        user = User.query.filter_by(username=form.username.data).first()
        if user and bcrypt.check_password_hash(user.password, form.password.data):
            login_user(user)
            next_page = request.args.get('next')
            return redirect(next_page) if next_page else redirect(url_for('home'))
        else:
            flash('Login Unsuccessful. Please check the username and password', 'danger')
    return render_template('login.html', title='Login', form=form)


def docker_refresh():
    # System Information
    # Get CPU information
    cpu = get_cpu_info()
    # Get Memory Information
    mem = virtual_memory()
    ram = mem.total / 1024 / 1024 / 1024
    # Connect to Docker Socket
    apiclient = docker.APIClient(base_url='unix://var/run/docker.sock')
    client = docker.from_env()

    # APIClient Informations [Low Level API]
    dockerinfo = apiclient.version()
    container_count = len(apiclient.containers(all=True))
    image_count = len(apiclient.images(all=True))
    docker_container = apiclient.containers(all=True)
    docker_image = apiclient.images(all=True)
    docker_volume = apiclient.volumes()
    docker_network = apiclient.networks()
    # High Level API
    volume_count = len(client.volumes.list())

    return [apiclient, client, dockerinfo, container_count, image_count, docker_container, docker_image, docker_volume,
            docker_network, volume_count, cpu, ram]


# Docker Home Page
@app.route('/home')
@login_required
def home():
    dock_func_list = docker_refresh()
    return render_template('home.html', title='Home', dockerinfo=dock_func_list[2], cpu=dock_func_list[10],
                           ram=dock_func_list[11], container_count=dock_func_list[3],
                           image_count=dock_func_list[4], volume_count=dock_func_list[9],
                           network_count=len(dock_func_list[8]))


# Change Password
@app.route('/change_password', methods=['GET', 'POST'])
@login_required
def change_password():
    form = ChangePasswordForm()
    user = current_user
    if form.validate_on_submit():
        hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
        user.password = hashed_password
        db.session.commit()
        flash(f"Your DockerAdmin password has been changed", 'success')
    return render_template('change_password.html', form=form, title='Change Password')


# Docker Containers
@app.route('/containers', methods=['GET', 'POST'])
@login_required
def containers():
    dock_func_list = docker_refresh()
    return render_template('containers.html', title='Containers', container_count=dock_func_list[3],
                           docker_container=dock_func_list[5])


# Docker Images
@app.route('/images', methods=['GET', 'POST'])
@login_required
def images():
    dock_func_list = docker_refresh()
    return render_template('images.html', title='Images', image_count=dock_func_list[4], docker_image=dock_func_list[6])


# Remove Docker Images
@app.route('/images/delete/<string:image_id>', methods=['GET', 'POST'])
@login_required
def delete_image(image_id):
    dock_func_list = docker_refresh()
    dock_func_list[0].remove_image(image_id)
    dock_func_list = docker_refresh()
    return render_template('images.html', title='Images', image_count=dock_func_list[4], docker_image=dock_func_list[6])


# Docker Volumes
@app.route('/volumes', methods=['GET', 'POST'])
@login_required
def volumes():
    dock_func_list = docker_refresh()
    return render_template('volumes.html', title='Volumes', volume_count=dock_func_list[9],
                           docker_volume=dock_func_list[7])


# Docker Networks
@app.route('/networks', methods=['GET', 'POST'])
@login_required
def networks():
    dock_func_list = docker_refresh()
    return render_template('networks.html', title='Networks', docker_network=dock_func_list[8],
                           network_count=len(dock_func_list[8]))


# Logout
@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('login'))


if __name__ == '__main__':
    app.run()
